// 系统级别的 Service 的调用接口

use common::{
    protocol::{protocol_v1::ProtocolV1Writer, MsgType},
    serial::request_message::RequestMessage,
    service::ServiceKey,
};
use tracing::info;

fn serial_v1_request<T>(receiver: &ServiceKey, message: &str, request_id: i64, param: &T) -> Vec<u8>
where
    T: RequestMessage,
{
    let sender = ServiceKey::default();
    //

    let string_size = protobuf::rt::string_size(1, message) ;

    let param_size = param.compute_size();

    let size = string_size + param_size;

    let mut v1_writer = ProtocolV1Writer::new(size as u32, request_id, &sender, receiver);

    v1_writer.write_msg_type(MsgType::System);
    //
    let msg_body_buffer = v1_writer.msg_body_buffer();
    //
    let mut os = protobuf::CodedOutputStream::bytes(msg_body_buffer);
    //
    //
    os.write_string(1, message).unwrap();

    //
    param.serial_with_output_stream(&mut os).unwrap();


    drop(os);

    v1_writer.msg_buffer
}

pub mod xport_service {
    use common::{
        base::{id_generator, status::Status},
        protocol::XID,
        serial::request_message::RequestMessage,
        service::{
            sys_service_api::xport_service::{ServiceInfo, ServiceInfos},
            ServiceKey,
        },
    };
    use lazy_static::lazy_static;
    use protobuf::rt::CachedSize;
    use std::sync::Arc;
    use tracing::info;

    use crate::airport::airport::Airport;

    pub struct ChannelEvent {
        pub channel_ip: String,
        pub channel_id: i64,
        pub is_active: bool,
        pub(crate) cached_size: protobuf::rt::CachedSize,
    }
    impl Default for ChannelEvent {
        fn default() -> Self {
            ChannelEvent {
                channel_ip: String::default(),
                channel_id: 0,
                is_active: false,
                cached_size: protobuf::rt::CachedSize::new(),
            }
        }
    }

    impl RequestMessage for ChannelEvent {
        fn compute_size(&self) -> u64 {
            let mut my_size = 0;
            if !self.channel_ip.is_empty() {
                my_size += protobuf::rt::string_size(1, &self.channel_ip);
            }
            if self.channel_id != 0 {
                my_size += protobuf::rt::int64_size(2, self.channel_id);
            }
            if self.is_active != false {
                my_size += 2 + 1;
            }
            self.cached_size.set(my_size as u32);
            my_size
        }

        fn serial_with_output_stream(
            &self,
            os: &mut protobuf::CodedOutputStream<'_>,
        ) -> Result<(), Status> {
            if !self.channel_ip.is_empty() {
                os.write_string(1, &self.channel_ip).unwrap();
            }
            if self.channel_id != 0 {
                os.write_int64(2, self.channel_id).unwrap();
            }
            if self.is_active != false {
                os.write_bool(3, self.is_active).unwrap();
            }
            Ok(())
        }

        fn parse_from_input_stream(
            &mut self,
            is: &mut protobuf::CodedInputStream<'_>,
        ) -> Result<(), Status> {
            while let Some(tag) = is.read_raw_tag_or_eof().unwrap() {
                match tag {
                    10 => {
                        self.channel_ip = is.read_string().unwrap();
                    }
                    16 => {
                        self.channel_id = is.read_int64().unwrap();
                    }
                    24 => {
                        self.is_active = is.read_bool().unwrap();
                    }
                    _ => {}
                }
            }
            Ok(())
        }
    }

    lazy_static! {
        static ref XPORT_SERVICE_KEY: ServiceKey =
            ServiceKey::new("XComService".into(), "0.0.1".into(), "XComService".into());
    }
    /**
     * 是否是主动连接
     */
    pub async fn channel_connected(
        airport: Arc<Box<Airport>>,
        channel_id: i64,
        channel_ip: String,
        is_active: bool,
    ) {
        //
        let channel_event = ChannelEvent {
            channel_id: channel_id,
            channel_ip: channel_ip,
            is_active: is_active,
            cached_size: CachedSize::default(),
        };
        let xid = XID::new(0, id_generator::gen_id());
        let buffer = super::serial_v1_request(
            &XPORT_SERVICE_KEY,
            "ChannelConnected",
            xid.request_id,
            &channel_event,
        );

        airport.add_request_handler(xid, Box::new(|_buffer| info!("通知 channel 连接成功...")));
        //
        airport.message_in(0, buffer).await;
    }

    pub async fn channel_disconnected(
        airport: Arc<Box<Airport>>,
        channel_id: i64,
        is_active: bool,
    ) {
        let channel_event = ChannelEvent {
            channel_id: channel_id,
            channel_ip: String::default(),
            is_active: is_active,
            cached_size: CachedSize::default(),
        };

        let xid = XID::new(0, id_generator::gen_id());

        let buffer = super::serial_v1_request(
            &XPORT_SERVICE_KEY,
            "ChannelDisConnected",
            xid.request_id,
            &channel_event,
        );

        airport.add_request_handler(xid, Box::new(|_buffer| info!("通知 channel 断开成功...")));
        airport.message_in(0, buffer).await;
    }

    pub async fn local_service_on(airport: Arc<Box<Airport>>, service_info: ServiceInfo) {
        let xid = XID::new(0, id_generator::gen_id());
        let buffer = super::serial_v1_request(
            &XPORT_SERVICE_KEY,
            "LocalServiceOn",
            xid.request_id,
            &service_info,
        );
        airport.add_request_handler(
            xid,
            Box::new(|_buffer| info!("通知 local_service 上线成功！...")),
        );
        info!("通知 service 上线....");
        airport.message_in(0, buffer).await;
    }

    pub async fn local_service_off(airport: Arc<Box<Airport>>, service_info: ServiceInfo) {
        let xid = XID::new(0, id_generator::gen_id());
        let buffer = super::serial_v1_request(
            &XPORT_SERVICE_KEY,
            "LocalServiceOff",
            xid.request_id,
            &service_info,
        );
        airport.add_request_handler(
            xid,
            Box::new(|_buffer| info!("通知 local_service 下线！...")),
        );
        airport.message_in(0, buffer).await;
    }
}
